"""
__author__ = 'loong'
"""

from collections import OrderedDict
from baye import models
import codecs
from io import BytesIO


PERSON_MAX = 2000
CITY_MAX = 255
TOOLS_MAX = 2000
TOOLS_QUEUE_MAX = 2000

ITEM_LEN_MAX = 65535


class GBKMap(object):
    def __init__(self, map):
        self.map = map

    def get(self, id):
        return decode_gbk(self.map.item(id))


def get_head(dat, resid):
    addr = get_addr(dat, resid)
    if addr == 4294967295:
        return None
    return read_struct(dat, addr, models.RESHEAD)


def get_addr(dat, resid):
    return read_struct(dat, (resid-1) * 4, models.ADDRESS)['address']


def read_int(s, n):
    r = 0
    for i in range(n):
        r += ord(s[i:i+1]) << (i * 8)
    return r


def write_int(num, n):
    s = b''
    for i in range(n):
        s += bytes([((num >> (i * 8)) & 0xff)])
    return s


def decode_ch(ch, key):
    return (ch - key + 256) % 256


def B(s):
    return codecs.decode(s, 'hex')

unknown_codes_map = [
    (B('c0eea2e3'), '李傕'),
    (B('cfc4baeea2ef'), '夏侯惇'),
    (B('b7d1a5f7'), '费祎'),
    (B('dcf7a2e4'), '荀彧'),
    (B('d5c5a5f8'), '张郃'),
    (B('c3aba5f9'), '毛玠'),
    (B('d1cfa5fa'), '严畯'),
    (B('b2dca2f0'), '曹叡'),
]


def encode_gbk(s):
    # mp = {s: b for b, s in unknown_codes_map}
    mp = {}
    return mp.get(s) or s.encode('gbk', 'replace')


def decode_gbk(data):
    b = data.split(b'\x00')[0]
    mp = dict(unknown_codes_map)
    return mp.get(b) or b.decode('gbk', errors='replace')


def decode_data(data, key):
    return bytes([decode_ch(c, key) for c in data])


def read_struct(dat, addr, model):
    rv = OrderedDict()
    cur = addr
    for p in model:
        if len(p) == 2:
            l, n = p
            rv[n] = read_int(dat[cur:], l)
        elif len(p) == 3:
            l, n, t = p
            if t == str:
                rv[n] = decode_gbk(dat[cur:cur+l])
            elif t == bytes:
                rv[n] = dat[cur:cur+l]
            else:
                raise NotImplementedError
        else:
            raise NotImplementedError
        cur += l
    return rv


def write_struct(dic, model):
    buf = b''
    for p in model:
        if len(p) == 2:
            l, n = p
            buf += write_int(dic[n], l)
        elif len(p) == 3:
            l, n, t = p
            if t == str:
                s = encode_gbk(dic[n] or '') + b'\x00' * 100
                buf += s[:l-1] + b'\x00'
            elif t == bytes:
                assert len(dic[n]) == l
                buf += dic[n]
            else:
                raise NotImplementedError
        else:
            raise NotImplementedError
    return buf


def sizeof(model):
    s = 0
    for n in model:
        s += n[0]
    return s


class DataIO:
    def __init__(self, data):
        self.io = BytesIO(data)
        self.len = len(data)

    def eof(self):
        return self.io.tell() == self.len

    def read(self, n):
        return self.io.read(n)

    def read_struct(self, model):
        data = self.read(sizeof(model))
        return read_struct(data, 0, model)

    def write(self, data):
        return self.io.write(data)

    def write_struct(self, model, data):
        if not isinstance(data, dict):
            data = data.__dict__
        self.write(write_struct(model=model, dic=data))

    def getvalue(self):
        return self.io.getvalue()
